package com.jy.oms.framework.asyn.task.web;

import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.jy.oms.framework.asyn.task.constants.TaskQueueTypeEnum;
import com.jy.oms.framework.asyn.task.constants.TaskStateEnum;
import com.jy.oms.framework.asyn.task.core.ExecutorConfig;
import com.jy.oms.framework.asyn.task.core.ExecutorContext;
import com.jy.oms.framework.asyn.task.handler.ITaskHandlerManager;
import com.jy.oms.framework.asyn.task.model.TaskDto;
import com.jy.oms.framework.asyn.task.model.TaskGroup;
import com.jy.oms.framework.asyn.task.model.TaskHandlerWrapper;
import com.jy.oms.framework.asyn.task.queue.TaskQueueManagerProxy;
import com.jy.oms.framework.asyn.task.queue.database.DbTaskQueueStatisticManager;
import com.jy.oms.framework.asyn.task.queue.database.entity.SysTaskQuery;
import com.jy.oms.framework.asyn.task.queue.database.entity.SysTaskQueue;
import com.jy.oms.framework.asyn.task.queue.database.service.ISysTaskQueueService;
import com.jy.oms.framework.util.DateUtils;
import com.jy.oms.framework.util.SpringUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 任务队列管理
 *
 * @author liuquan
 */
@WebServlet("/taskQueue/status")
public class TaskQueueServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String action = request.getParameter("action");
        if (StringUtils.isBlank(action)) {
            print(request, response, TaskQueueResponse.failure("参数[action]不能为空"));
            return;
        }

        if (StringUtils.equalsIgnoreCase(action, "listConfig")) {
            queryConfig(request, response);
            return;
        }

        if (StringUtils.equalsIgnoreCase(action, "listTaskQueue")) {
            queryTaskQueueList(request, response);
            return;
        }

        if (StringUtils.equalsIgnoreCase(action, "execute")) {
            executeTaskQueue(request, response);
            return;
        }

        if (StringUtils.equalsIgnoreCase(action, "editTask")) {
            editTaskQueue(request, response);
            return;
        }

    }

    private void queryConfig(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ITaskHandlerManager taskHandlerManager = SpringUtil.getBean(ITaskHandlerManager.class);
        Map<TaskGroup, List<TaskHandlerWrapper>> groupListMap = taskHandlerManager.loadAllExecutableTaskHandler();
        List<String> taskCodes = groupListMap.entrySet().stream()
                .filter(entry -> entry.getKey().getTaskQueueType() == TaskQueueTypeEnum.DB)
                .map(Map.Entry::getValue)
                .flatMap(Collection::stream)
                .map(TaskHandlerWrapper::getTaskCode)
                .collect(Collectors.toList());

        List<String> taskQueueNames = groupListMap.entrySet().stream()
                .filter(entry -> entry.getKey().getTaskQueueType() == TaskQueueTypeEnum.DB)
                .map(Map.Entry::getValue)
                .flatMap(Collection::stream)
                .map(e -> e.getExecuteStrategy().getTaskGroupDbQueueName())
                .filter(StringUtils::isNotBlank)
                .distinct()
                .collect(Collectors.toList());

        TaskQueueConfigVo taskQueueConfigVo = new TaskQueueConfigVo();
        taskQueueConfigVo.setTaskCodes(taskCodes);
        taskQueueConfigVo.setTaskQueueNames(taskQueueNames);

        print(request, response, TaskQueueResponse.success(taskQueueConfigVo));
    }

    private void queryTaskQueueList(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Map<String, String> queryMap = ServletUtil.getParamMap(request);
        String taskCode = StringUtils.trimToNull(queryMap.get("taskCode"));
        String billCode = StringUtils.trimToNull(queryMap.get("billCode"));
        String startTime = StringUtils.trimToNull(queryMap.get("startTime"));
        String endTime = StringUtils.trimToNull(queryMap.get("endTime"));
        String state = StringUtils.trimToNull(queryMap.get("state"));
        String taskQueueName = StringUtils.trimToNull(queryMap.get("taskQueueName"));
        if (StringUtils.isAnyBlank(startTime, endTime, state)) {
            print(request, response, TaskQueueResponse.failure("参数[taskCode、startTime、endTime、state]为空"));
            return;
        }

        Date startTimeD;
        Date endTimeD;
        try {
            startTimeD = DateUtils.string2Date(startTime, "yyyy/MM/dd HH:mm");
            endTimeD = DateUtils.string2Date(endTime, "yyyy/MM/dd HH:mm");
        } catch (Exception e) {
            print(request, response, TaskQueueResponse.failure("开始时间、结束时间格式错误"));
            return;
        }
        if (startTimeD.after(endTimeD)) {
            print(request, response, TaskQueueResponse.failure("开始时间要早于结束时间"));
            return;
        }

        ExecutorConfig executorConfig = ExecutorContext.getExecutorConfig();
        if(executorConfig == null){
            print(request, response, TaskQueueResponse.failure("当前应用不能查看异步任务信息"));
            return;
        }

        SysTaskQuery sysTaskQuery = new SysTaskQuery();
        sysTaskQuery.setBusinessGroup(executorConfig.getBusinessGroup());
        sysTaskQuery.setEnvFlag(executorConfig.getEnv());
        sysTaskQuery.setTaskCode(taskCode);
        sysTaskQuery.setBillCode(billCode);
        sysTaskQuery.setStartTime(startTimeD);
        sysTaskQuery.setEndTime(endTimeD);
        int stateInt = Integer.parseInt(state);
        if (TaskStateEnum.between(stateInt, TaskStateEnum.ERROR)) {
            sysTaskQuery.setStates(Stream.of(TaskStateEnum.ERROR.getCode(), TaskStateEnum.TIMEOUT.getCode()).collect(Collectors.toList()));
        } else {
            sysTaskQuery.setState(stateInt);
        }
        sysTaskQuery.setPageSize(20);

        ISysTaskQueueService sysTaskQueueService = SpringUtil.getBean(ISysTaskQueueService.class);
        List<SysTaskQueue> sysTaskQueueList = sysTaskQueueService.querySysTaskList(sysTaskQuery, taskQueueName);
        List<TaskQueueVo> resultList = sysTaskQueueList.stream().map(task -> {
            TaskQueueVo sysTaskQueueVo = TaskQueueVo.convertSysTaskQueue(task);
            sysTaskQueueVo.setTaskQueueName(taskQueueName);
            return sysTaskQueueVo;
        }).collect(Collectors.toList());

        print(request, response, TaskQueueResponse.success(resultList));
    }

    private void executeTaskQueue(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Map<String, String> queryMap = ServletUtil.getParamMap(request);
        String idsStr = queryMap.get("ids");
        if (StringUtils.isBlank(idsStr)) {
            print(request, response, TaskQueueResponse.failure("参数[ids]不能为空"));
            return;
        }

        List<Long> ids = Arrays.stream(idsStr.split(","))
                .filter(StringUtils::isNotBlank)
                .map(Long::parseLong).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(ids)) {
            print(request, response, TaskQueueResponse.failure("参数[ids]不能为空"));
            return;
        }

        ExecutorConfig executorConfig = ExecutorContext.getExecutorConfig();
        if(executorConfig == null){
            print(request, response, TaskQueueResponse.failure("当前应用不能编辑异步任务信息"));
            return;
        }

        String taskQueueName = StringUtils.trimToEmpty(queryMap.get("taskQueueName"));
        SysTaskQuery sysTaskQuery = new SysTaskQuery();
        sysTaskQuery.setIds(ids);
        ISysTaskQueueService sysTaskQueueService = SpringUtil.getBean(ISysTaskQueueService.class);
        List<SysTaskQueue> sysTaskQueueList = sysTaskQueueService.querySysTaskList(sysTaskQuery, taskQueueName);
        if (CollectionUtils.isEmpty(sysTaskQueueList)) {
            print(request, response, TaskQueueResponse.failure("任务不存在"));
            return;
        }

        List<SysTaskQueue> updateSysTaskList = sysTaskQueueList.stream()
                .filter(task -> !TaskStateEnum.between(task.getState(), TaskStateEnum.DONE))
                .map(task -> {
                    SysTaskQueue queue = new SysTaskQueue();
                    queue.setId(task.getId());
                    queue.setTaskGroup(task.getTaskGroup());
                    queue.setCallCount(0);
                    queue.setState(TaskStateEnum.UNDO.getCode());
                    queue.setEarliestExecuteTime(new Date());
                    return queue;
                }).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(updateSysTaskList)) {
            print(request, response, TaskQueueResponse.failure("任务都已经执行过"));
            return;
        }

        DbTaskQueueStatisticManager dbTaskQueueStatisticManager = SpringUtil.getBean(DbTaskQueueStatisticManager.class);
        updateSysTaskList.stream().forEach(task -> dbTaskQueueStatisticManager.resetTask(task, taskQueueName));

        print(request, response, TaskQueueResponse.success().setMsg("执行成功"));
    }

    private void editTaskQueue(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Map<String, String> queryMap = ServletUtil.getParamMap(request);
        String taskId = StringUtils.trimToNull(queryMap.get("taskId"));
        String taskCode = StringUtils.trimToNull(queryMap.get("taskCode"));
        String taskParam = StringUtils.trimToNull(queryMap.get("taskParam"));
        String billCode = StringUtils.trimToNull(queryMap.get("billCode"));
        String executeTime = StringUtils.trimToNull(queryMap.get("executeTime"));
        String taskQueueName = StringUtils.trimToNull(queryMap.get("taskQueueName"));

        Date executeTimeD = null;
        if (StringUtils.isNotBlank(executeTime)) {
            try {
                executeTimeD = DateUtils.string2Date(executeTime, "yyyy/MM/dd HH:mm");

                if (executeTimeD.before(new Date())) {
                    print(request, response, TaskQueueResponse.failure("执行时间不能早于当前时间"));
                    return;
                }
            } catch (Exception e) {
                print(request, response, TaskQueueResponse.failure("执行时间格式错误"));
                return;
            }
        }

        if (StringUtils.isAnyBlank(taskCode, taskParam)) {
            print(request, response, TaskQueueResponse.failure("参数[taskCode、taskParam]为空"));
            return;
        }

        ExecutorConfig executorConfig = ExecutorContext.getExecutorConfig();
        if(executorConfig == null){
            print(request, response, TaskQueueResponse.failure("当前应用不能编辑异步任务信息"));
            return;
        }

        ISysTaskQueueService sysTaskQueueService = SpringUtil.getBean(ISysTaskQueueService.class);
        if (StringUtils.isNotBlank(taskId)) {
            SysTaskQuery sysTaskQuery = new SysTaskQuery();
            sysTaskQuery.setId(Long.parseLong(taskId));
            List<SysTaskQueue> sysTaskQueueList = sysTaskQueueService.querySysTaskList(sysTaskQuery, taskQueueName);
            if (CollectionUtils.isEmpty(sysTaskQueueList)) {
                print(request, response, TaskQueueResponse.failure("任务不存在"));
                return;
            }

            SysTaskQueue sysTaskQueueDb = sysTaskQueueList.get(0);
            if (!StringUtils.equals(sysTaskQueueDb.getTaskCode(), taskCode)) {
                print(request, response, TaskQueueResponse.failure("taskCode与数据库记录不一致"));
                return;
            }

            SysTaskQueue updSysTaskQueue = new SysTaskQueue();
            updSysTaskQueue.setId(sysTaskQueueDb.getId());
            updSysTaskQueue.setTaskParam(taskParam);
            updSysTaskQueue.setUpdateDate(new Date());
            sysTaskQueueService.updateById(updSysTaskQueue, taskQueueName);

            print(request, response, TaskQueueResponse.success().setMsg("修改任务成功"));
            return;
        } else {
            ITaskHandlerManager taskHandlerManager = SpringUtil.getBean(ITaskHandlerManager.class);
            TaskHandlerWrapper taskHandlerWrapper = taskHandlerManager.loadTaskHandler(taskCode);
            if (taskHandlerWrapper == null) {
                print(request, response, TaskQueueResponse.failure("[" + taskCode + "]对应的任务不存在"));
                return;
            }

            boolean parseResult = false;
            try {
                Class<?> paramType = taskHandlerWrapper.getTaskHandlerParamType();
                Object taskParamObj;
                if (paramType == String.class) {
                    taskParamObj = taskParam;
                } else {
                    taskParamObj = JSON.parseObject(taskParam, paramType);
                }
                parseResult = true;

                long delayedSeconds = executeTimeD != null ? (executeTimeD.getTime() - new Date().getTime()) / 1000 : 0;
                TaskDto taskDto = TaskDto.create()
                        .setTaskCode(taskCode)
                        .setBillCode(billCode)
                        .setParam(taskParamObj)
                        .setDelayedSeconds(delayedSeconds);
                TaskQueueManagerProxy.addTask(taskDto);
                print(request, response, TaskQueueResponse.success().setMsg("新增任务成功"));
                return;
            } catch (Exception e) {
                if (!parseResult) {
                    print(request, response, TaskQueueResponse.failure("任务参数不能被解析"));
                    return;
                }

                print(request, response, TaskQueueResponse.failure(e.getMessage()));
                return;
            }
        }
    }

    private void print(HttpServletRequest request, HttpServletResponse response, Object msg) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        PrintWriter writer = response.getWriter();
        writer.write(JSON.toJSONString(msg));
        writer.flush();
        writer.close();
    }

}
